package com.hzw.saas.service.hpc.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.hpc.*;
import com.hzw.saas.api.hpc.bo.*;
import com.hzw.saas.api.hpc.enums.*;
import com.hzw.saas.api.hpc.query.*;
import com.hzw.saas.api.user.ICustomerService;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.api.user.bo.CustomerBO;
import com.hzw.saas.api.user.bo.UserAccountBO;
import com.hzw.saas.common.config.annotation.RedisProcessLock;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.config.util.PageUtils;
import com.hzw.saas.common.config.util.RedisUtil;
import com.hzw.saas.common.util.Arith;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.service.hpc.mapper.HpcContractMapper;
import com.hzw.saas.service.hpc.model.HpcContract;
import com.hzw.saas.service.hpc.service.IQCloudHpcAccountService;
import com.hzw.saas.service.hpc.service.IQingCloudService;
import com.hzw.saas.service.hpc.service.query.QCloudHpcAccountQuery;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 合同登记业务实现
 *
 * @Description
 * @Date 2021/7/15 5:58 下午
 * @Created by sonam
 */
@RequiredArgsConstructor
@Service("hpcContractImpl")
public class HpcContractImpl extends ServiceImpl<HpcContractMapper, HpcContract> implements IHpcContractService, IService<HpcContract> {
    private static final String CONTRACT_CACHE_NAME = "HPC:CONTRACT";

    @Resource(name = "hpcAccountServiceImpl")
    private IHpcAccountService hpcAccountService;
    @Resource(name = "hpcAccountUserServiceImpl")
    private IHpcAccountUserService hpcAccountUserService;
    @Resource(name = "userAccountServiceImpl")
    private IUserAccountService accountService;
    @Resource(name = "customerServiceImpl")
    private ICustomerService customerService;
    @Resource(name = "hpcAccountUserServiceImpl")
    private IHpcAccountUserService accountUserService;
    @Resource(name = "hpcAccountRecordServiceImpl")
    private IHpcAccountRecordService accountRecordService;
    @Resource(name = "hpcAccountProviderServiceImpl")
    private IHpcAccountProviderService providerService;
    @Resource(name = "qCloudHpcAccountServiceImpl")
    private IQCloudHpcAccountService qcHpcAccountService;
    @Resource(name = "qingCloudServiceImpl")
    private final IQingCloudService qingCloudService;
    @Resource(name = "hpcContractImpl")
    private IHpcContractService self;

    private final ApplicationEventPublisher publisher;

    @Override
    public List<HpcContractBO> query(HpcContractBO hpcContractBO) {
        if (hpcContractBO == null) return new ArrayList<>();
        List<HpcContract> hpcContracts = this.lambdaQuery().setEntity(MapperUtil.nf().map(hpcContractBO, HpcContract.class)).list();
        return MapperUtil.nf().mapAsList(hpcContracts, HpcContractBO.class);
    }

    @Override
    @Transactional
    @CacheEvict(cacheNames = {CONTRACT_CACHE_NAME}, key = "#p0.contractId")
    @RedisProcessLock(lockName = "CreateContract", key = "#createHpcContractQuery.hpcAccountUserId + ':' + #createHpcContractQuery.contractId")
    public HpcContractBO boundContract(@Validated CreateHpcContractQuery createHpcContractQuery) {
        AssertUtil.assertThrow("参数错误", Objects.isNull(createHpcContractQuery));
        String contractId = createHpcContractQuery.getContractId();
        HpcContractBO hpcContractBO = self.getById(contractId);
        this.checkContract(hpcContractBO);

        String hpcAccountUserId = createHpcContractQuery.getHpcAccountUserId();
        HpcAccountUserBo hpcAccountUserBo = accountUserService.get(hpcAccountUserId);
        AssertUtil.assertThrow("绑定的HPC用户不存在", Objects.isNull(hpcAccountUserBo) || hpcAccountUserBo.getIsMain() == HpcAccountUserType.SUB.value());
        AssertUtil.assertThrow("合同信息与用户信息不一致", !Objects.equals(hpcAccountUserBo.getUserId(), hpcContractBO.getUserId()) || hpcAccountUserBo.getIsMain() == 0);

        // 设置HPC与合同关联信息
        String hpcAccountRecordId = hpcAccountUserBo.getHpcAccountRecordId();
        HpcAccountRecordBo hpcAccountRecordBo = accountRecordService.get(hpcAccountRecordId);
        hpcContractBO.setHpcAccount(hpcAccountRecordBo.getHpcAccountSnapshot());
        hpcContractBO.setHpcAccountId(hpcAccountRecordBo.getHpcAccountId());
        hpcContractBO.setHpcAccountRecordId(hpcAccountRecordId);

        // 判断合同类型是否和HPC账号类型一致
        HpcAccountBo hpcAccountBo = hpcAccountService.getById(hpcAccountRecordBo.getHpcAccountId());
        if (Objects.nonNull(hpcAccountBo)) {
            HpcAccountProviderBo providerBo = providerService.getById(hpcAccountBo.getProviderId());
            if (Objects.nonNull(providerBo)) {
                String providerCode = providerBo.getProviderCode();
                AssertUtil.assertThrow(StrUtil.format("绑定失败，{}合同不能绑定{}账号", hpcContractBO.getProviderCode(), providerCode), !Objects.equals(providerCode, hpcContractBO.getProviderCode()));
            }
        }

        // 查询是否存在分配的HPC账号登记的合同数量，存在则续费，不存在则根据合同信息创建合同并更新HPC用户信息
        Integer count = this.lambdaQuery()
            .eq(HpcContract::getHpcAccountRecordId, hpcAccountUserBo.getHpcAccountRecordId())
            .eq(HpcContract::getUserId, hpcAccountUserBo.getUserId())
            .ne(HpcContract::getHpcAccountId, "").count();

        // accountUserId不存在合同，不做核时累加
        Date contractExpireTime = DateUtil.offsetSecond(DateUtil.endOfDay(hpcContractBO.getExpireTime()), -1);
        if (count == null || count == 0) {
            // 消耗核时
            double useCoreTime = Arith.sub(hpcAccountRecordBo.getCoreTimeBalanceTotal(), hpcAccountRecordBo.getCoreTimeBalance());
            // 更新 hpcRecord与合同信息保持一致
            hpcAccountRecordBo.setExpireTime(contractExpireTime);
            hpcAccountRecordBo.setCoreTimeBalanceTotal(hpcContractBO.getCoreTime());
            hpcAccountRecordBo.setSyncTime(DateUtil.parseDateTime("2021-01-01 00:00:00"));
            hpcAccountRecordBo.setCoreTimeBalance(Arith.sub(hpcContractBO.getCoreTime(), useCoreTime));
            if (hpcContractBO.getBusTime() != null) {
                hpcAccountRecordBo.setCreateTime(hpcContractBO.getBusTime());
            }
            accountRecordService.update(hpcAccountRecordBo);
            // 更新HpcAccountUser状态为非冻结
            HpcAccountUserBo upAccountUserQuery = new HpcAccountUserBo();
            upAccountUserQuery.setStatus(HpcAccountStatus.NORMAL.value());
            upAccountUserQuery.setPid(hpcAccountUserId);
            hpcAccountUserService.update(upAccountUserQuery);
        } else {
            // 续期
            HpcAccountRenewalQuery hpcAccountRenewalQuery = new HpcAccountRenewalQuery();
            hpcAccountRenewalQuery.setCoreTime(hpcContractBO.getCoreTime());
            hpcAccountRenewalQuery.setGiveCoreTime(0d);
            hpcAccountRenewalQuery.setPid(hpcAccountUserBo.getPid());
            if (hpcContractBO.getBusTime() != null) {
                hpcAccountRenewalQuery.setBusDate(hpcContractBO.getBusTime());
            }

            // 获取当前用户对应账号合同最晚的过期时间
            QueryWrapper<HpcContract> queryWrapper = new QueryWrapper<>();
            queryWrapper.select("max(expire_time) as expire_time")
                .lambda().eq(HpcContract::getHpcAccountId, hpcContractBO.getHpcAccountId())
                .eq(HpcContract::getUserId, hpcContractBO.getUserId());
            HpcContract hpcContract = this.getOne(queryWrapper);
            Date expireTime = hpcContract.getExpireTime();

            hpcAccountRenewalQuery.setExpireDate(DateUtil.compare(contractExpireTime, expireTime) > 0 ? contractExpireTime : expireTime);

            // 续期
            accountRecordService.renewHpcAccountUser(hpcAccountRenewalQuery);
        }

        // 更新
        this.updateById(MapperUtil.nf().map(hpcContractBO, HpcContract.class));

        return hpcContractBO;
    }

    @Override
    @CachePut(cacheNames = {CONTRACT_CACHE_NAME}, key = "#result.pid", condition = "#result != null and #result.pid != ''")
    public HpcContractBO registrationOrUpdateContract(@Validated ContractRegistrationQuery contractRegistrationQuery) {
        if (Objects.isNull(contractRegistrationQuery)) {
            return null;
        }
        String userId = contractRegistrationQuery.getUserId();
        UserAccountBO account = accountService.getAccount(userId);
        AssertUtil.assertThrow("操作失败，用户不存在", Objects.isNull(account));

        if (StrUtil.isNotBlank(contractRegistrationQuery.getPid())) {
            HpcContractBO hpcContractBO = self.getById(contractRegistrationQuery.getPid());
            AssertUtil.assertThrow("操作失败，合同不存在", Objects.isNull(hpcContractBO));
            AssertUtil.assertThrow("操作失败，合同已绑定，不能修改", StrUtil.isNotBlank(hpcContractBO.getHpcAccountId()));
            if (!Objects.equals(hpcContractBO.getContractNo(), contractRegistrationQuery.getContractNo())) {
                AssertUtil.assertThrow("操作失败，合同已存在", this.lambdaQuery().eq(HpcContract::getContractNo, contractRegistrationQuery.getContractNo()).count() != 0);
            }

            if (!Objects.equals(hpcContractBO.getContractName(), contractRegistrationQuery.getContractName())) {
                AssertUtil.assertThrow("操作失败，合同已存在", this.lambdaQuery().eq(HpcContract::getContractName, contractRegistrationQuery.getContractName()).count() != 0);
            }
        }
        HpcContract contract = MapperUtil.nf().map(contractRegistrationQuery, HpcContract.class);
        // 设置合同类型
        contract.setContractType(HpcContractType.FORMAL.value());
        if (null != contractRegistrationQuery) {
            contract.setContractType(contractRegistrationQuery.getHpcContractType().value());
        }
        if (StrUtil.isBlank(contract.getPid())) {
            // 添加
            contract.setPid(SnowFlakeUtil.nextIdStr());
            checkExistContract(contract);
        }

        // 默认值
        contract.setHpcAccount("");
        contract.setHpcAccountId("");
        contract.setHpcAccountRecordId("");

        if (contract.getBusTime() == null) {
            contract.setBusTime(new Date());
        }

        // 设置用户信息
        CustomerBO customerBO = customerService.getCustomerByUserId(account.getUserId());
        AssertUtil.assertThrow("客户信息丢失", Objects.isNull(customerBO));
        Optional<CustomerBO> optional = Optional.ofNullable(customerBO);
        contract.setRealName(optional.map(CustomerBO::getCustomerName).orElse(""));
        contract.setUserName(optional.map(CustomerBO::getUserName).orElse(""));
        contract.setEmail(optional.map(CustomerBO::getEmail).orElse(""));
        contract.setPhoneNo(optional.map(CustomerBO::getPhoneNo).orElse(""));
        contract.setExpireTime(DateUtil.offsetSecond(DateUtil.endOfDay(contract.getExpireTime()), -1));

        this.saveOrUpdate(contract);
        return MapperUtil.nf().map(contract, HpcContractBO.class);
    }

    private void checkExistContract(HpcContract contract) {
        if (contract.getContractType().equals(HpcContractType.TRIAL.value())) {
            // 试用合同不检查
            return;
        }
        String contractNo = contract.getContractNo();
        AssertUtil.assertThrow(StrUtil.format("操作失败，合同{}已存在", contractNo), this.lambdaQuery().eq(HpcContract::getContractNo, contractNo).count() != 0);
        String contractName = contract.getContractName();
        AssertUtil.assertThrow(StrUtil.format("操作失败，合同{}已存在", contractName), this.lambdaQuery().eq(HpcContract::getContractName, contractName).count() != 0);
    }

    @Override
    @Cacheable(cacheNames = {CONTRACT_CACHE_NAME}, key = "#p0", condition = "#p0 != null or #p0 != ''")
    public HpcContractBO getById(String contractId) {
        if (StrUtil.isBlank(contractId)) {
            return null;
        }
        HpcContract hpcContract = super.getById(contractId);
        return MapperUtil.nf().map(hpcContract, HpcContractBO.class);
    }

    @Override
    @CacheEvict(cacheNames = {CONTRACT_CACHE_NAME}, key = "#p0", condition = "#p0 != null and #p0 != ''")
    public void deleteCacheById(String contractId) {
    }

    @Override
    public HpcContractBO checkContract(String contractId) {
        return checkContract(self.getById(contractId));
    }

    @Override
    public HpcContractBO checkContract(HpcContractBO hpcContract) {
        // 校验合同是否有效
        AssertUtil.assertThrow("合同无效，合同不存在", Objects.isNull(hpcContract));
        AssertUtil.assertThrow("合同已绑定", StrUtil.isNotBlank(hpcContract.getHpcAccountId()));
        AssertUtil.assertThrow("合同无效，合同过期", Objects.isNull(hpcContract.getExpireTime()) || DateUtil.compare(DateUtil.date(), hpcContract.getExpireTime()) > 0);
        AssertUtil.assertThrow("合同无效，合同金额不能小于零", Objects.isNull(hpcContract.getContractAmount()) || hpcContract.getContractAmount() < 0d);
        AssertUtil.assertThrow("合同无效，用户不存在", !accountService.existAccount(hpcContract.getUserId()));
        AssertUtil.assertThrow("合同无效，用户真实姓名不能为空", !accountService.existAccount(hpcContract.getUserId()));
        AssertUtil.assertThrow("合同无效，用户电话号码和用户邮箱不能都为空", StrUtil.isBlank(hpcContract.getEmail()) && StrUtil.isBlank(hpcContract.getPhoneNo()));
        AssertUtil.assertThrow("合同无效，合同名称不能为空", StrUtil.isBlank(hpcContract.getContractName()));
        AssertUtil.assertThrow("合同无效，合同编号不能为空", StrUtil.isBlank(hpcContract.getContractNo()));
        AssertUtil.assertThrow("合同无效，合同核时不能为空", Objects.isNull(hpcContract.getCoreTime()) || hpcContract.getCoreTime() <= 0d);
        return hpcContract;
    }

    @Override
    @Transactional
    @CacheEvict(cacheNames = {CONTRACT_CACHE_NAME}, key = "#p0.contractId")
    @RedisProcessLock(lockName = "BoundHpcAccount", key = "#contractUsableQuery.hpcAccountId")
    public boolean contractUsable(@Validated ContractUsableQuery contractUsableQuery) {
        String contractId = contractUsableQuery.getContractId();
        String hpcAccountId = contractUsableQuery.getHpcAccountId();
        String slurmAccount = contractUsableQuery.getSlurmAccount();
        // 校验HPC账号是否有效
        HpcContractBO hpcContractBO = checkContract(contractId);
        // 校验HpcAccountId是否有效
        HpcAccountBo hpcAccountBo = hpcAccountService.getAccount(new HpcAccountBo().setHpcAccountId(hpcAccountId));
        AssertUtil.assertThrow("选择的HPC账号不存在", Objects.isNull(hpcAccountBo));
        HpcAccountRecordBo hpcAccountRecordBo = accountRecordService.searchOneByHpcAccountId(hpcAccountId);
        // 是否被占用
        AssertUtil.assertThrow("指定的HPC账号被占用", Objects.nonNull(hpcAccountRecordBo));
        // 检查合同与HPC账号的类型是否一致
        String providerId = hpcAccountBo.getProviderId();
        if (StrUtil.isNotBlank(providerId)) {
            HpcAccountProviderBo providerBo = providerService.getById(providerId);
            if (Objects.nonNull(providerBo)) {
                String providerCode = providerBo.getProviderCode();
                AssertUtil.assertThrow(StrUtil.format("绑定失败，{}合同不能绑定{}账号", hpcContractBO.getProviderCode(), providerCode), !Objects.equals(hpcContractBO.getProviderCode(), providerCode));
                AssertUtil.assertThrow("绑定失败，没有指定slurm队列", Objects.equals(HpcProviderType.QY.value(), providerCode) && StrUtil.isBlank(slurmAccount));
            }
        }
        // 设置分配的HPC账号
        hpcContractBO.setHpcAccount(hpcAccountBo.getHpcAccount());
        hpcContractBO.setHpcAccountId(hpcAccountBo.getHpcAccountId());
        // 添加或者更新HPC-USER余额
        Double coreTime = hpcContractBO.getCoreTime();
        // 构建分配HPC帐号参数
        MainHpcAccountQuery mainHpcAccountQuery = new MainHpcAccountQuery();
        mainHpcAccountQuery
            .setCanShare(1) // 能分享
            .setIncludeCurDay(true)
            .setIsInfinite(hpcContractBO.getIsInfinite() == null ? 0 : hpcContractBO.getIsInfinite()) // 默认有限算力
            .setUserId(hpcContractBO.getUserId())
            .setExpireTime(hpcContractBO.getExpireTime())
            .setCoreTimeBalance(coreTime)
            .setCoreTimeBalanceTotal(coreTime)
            .setSlurmAccount(slurmAccount)
            .setHpcAccountId(hpcAccountId);
        // 设置hpcRecordId
        HpcAccountUserBo hpcAccountUserBo = accountUserService.addMainAccountUser(mainHpcAccountQuery);

        String hpcAccountRecordId = hpcAccountUserBo.getHpcAccountRecordId();
        hpcContractBO.setHpcAccountRecordId(hpcAccountRecordId);
        HpcAccountRecordBo recordBo = new HpcAccountRecordBo();
        recordBo.setPid(hpcAccountRecordId).setCreateTime(hpcContractBO.getBusTime());
        accountRecordService.update(recordBo);
        return this.updateById(MapperUtil.nf().map(hpcContractBO, HpcContract.class));
    }

    @SneakyThrows
    @Override
    @Transactional
    @RedisProcessLock(lockName = "BoundHpcAccount", key = "#trialContractQuery.hpcAccountId")
    public void trialContract(TrialContractQuery trialContractQuery) {
        String hpcAccountId = trialContractQuery.getHpcAccountId();
        AssertUtil.assertThrow("操作频繁，稍后重试!", isLockHpcAccount(hpcAccountId));
        try {
            // 锁定账号一段时间不能再次进行绑定
            lockHpcAccount(hpcAccountId, 60);
            doTrialContract(trialContractQuery);
        } catch (Exception e) {
            // 异常则解除锁定
            unLockHpcAccount(hpcAccountId);
            throw e;
        }
    }

    private void doTrialContract(TrialContractQuery trialContractQuery) {
        AssertUtil.assertThrow("使用合同失败，用户不存在", Objects.isNull(customerService.getCustomerByUserId(trialContractQuery.getUserId())));
        // 创建合同
        ContractRegistrationQuery contractRegistrationQuery = new ContractRegistrationQuery();
        contractRegistrationQuery.setContractName("算力试用");
        contractRegistrationQuery.setContractNo(SnowFlakeUtil.nextIdStr());
        contractRegistrationQuery.setContractAmount(0.0D);
        contractRegistrationQuery.setCoreTime(trialContractQuery.getCoreTime());
        contractRegistrationQuery.setUserId(trialContractQuery.getUserId());
        contractRegistrationQuery.setIsInfinite(0);
        contractRegistrationQuery.setExpireTime(DateUtil.offsetDay(new Date(), trialContractQuery.getExpireDay()));
        contractRegistrationQuery.setBusTime(new Date());
        contractRegistrationQuery.setHpcContractType(HpcContractType.TRIAL);
        contractRegistrationQuery.setProviderCode(trialContractQuery.getProviderCode());
        HpcContractBO contractBO = self.registrationOrUpdateContract(contractRegistrationQuery);

        // 合同试用
        // 检查hpcAccount是否存在试用合同
        String hpcAccountId = trialContractQuery.getHpcAccountId();
//        List<HpcContract> hpcContracts = this.lambdaQuery().eq(HpcContract::getHpcAccountId, hpcAccountId).eq(HpcContract::getContractType, HpcContractType.TRIAL.value()).list();
//        AssertUtil.assertThrow("该HPC账号存在试用合同，请查询选择HPC账号", CollectionUtil.isNotEmpty(hpcContracts));

        HpcAccountUserBo hpcAccountUserBo = hpcAccountUserService.get(contractBO.getUserId(), hpcAccountId);
        if (hpcAccountUserBo == null) {
            // 合同使用
            ContractUsableQuery contractUsableQuery = new ContractUsableQuery();
            contractUsableQuery.setContractId(contractBO.getPid());
            contractUsableQuery.setHpcAccountId(trialContractQuery.getHpcAccountId());
            contractUsableQuery.setSlurmAccount(trialContractQuery.getSlurmAccount());
            this.contractUsable(contractUsableQuery);
        } else {
            // 续费/绑定合同
            CreateHpcContractQuery createHpcContractQuery = new CreateHpcContractQuery();
            createHpcContractQuery.setContractId(contractBO.getPid());
            createHpcContractQuery.setHpcAccountUserId(hpcAccountUserBo.getPid());
            this.boundContract(createHpcContractQuery);
        }
    }

    /**
     * 锁定ID
     */
    private void lockHpcAccount(String hpcAccountId, Integer lockTime) {
        RedisUtil.set("LOCK:" + hpcAccountId, 1, lockTime);
    }

    private void unLockHpcAccount(String hpcAccountId) {
        RedisUtil.del("LOCK:" + hpcAccountId);
    }

    private boolean isLockHpcAccount(String hpcAccountId) {
        return RedisUtil.hasKey("LOCK:" + hpcAccountId);
    }

    @Override
    @CacheEvict(cacheNames = {CONTRACT_CACHE_NAME}, key = "#p0", condition = "#p0 != null or p0 != ''")
    public boolean deleteContract(String pid, boolean deleteExpire) {
        if (StrUtil.isBlank(pid)) {
            return false;
        }
        HpcContractBO hpcContractBO = self.getById(pid);
        if (Objects.isNull(hpcContractBO)) {
            return false;
        }
        // 是否强制删除过期合同
        Date expireTime = hpcContractBO.getExpireTime();
        if (deleteExpire && (Objects.isNull(expireTime) || DateUtil.compare(DateUtil.date(), expireTime) > 0)) {
            return this.removeById(pid);
        }
        // 判断是否已绑定HPC账号
        AssertUtil.assertThrow("删除合同失败，合同已生效", StrUtil.isNotBlank(hpcContractBO.getHpcAccountId()));
        // 正常删除
        return this.removeById(pid);
    }

    @Override
    @Transactional
    public boolean deleteContracts(List<String> contractIds, boolean deleteExpire) {
        if (CollectionUtil.isEmpty(contractIds)) {
            return false;
        }
        contractIds.forEach(contractId -> {
            if (StrUtil.isBlank(contractId)) {
                return;
            }
            self.deleteContract(contractId, deleteExpire);
        });
        return true;
    }

    @Override
    public IPage<HpcContractBO> listByUserIds(PageParam pageParam, List<String> userIds, String keyword, Integer status, Integer contractType, List<String> prodviderCodes) {
        return this.listByUserIds(pageParam, userIds, keyword, status, contractType, prodviderCodes, null);
    }

    @Override
    public IPage<HpcContractBO> listByUserIds(PageParam pageParam, List<String> userIds, String keyword, Integer status, Integer contractType, List<String> prodviderCodes, String creatorId) {
        List<String> userIdsExtra = customerService.listCustomer2UserId(keyword);

        IPage<HpcContract> hpcContracts = this.lambdaQuery()
            .in(CollectionUtils.isNotEmpty(userIds), HpcContract::getUserId, userIds)
            .in(CollectionUtils.isNotEmpty(prodviderCodes), HpcContract::getProviderCode, prodviderCodes)
            .eq(StrUtil.isNotBlank(creatorId), HpcContract::getCreatorId, creatorId)
            .and(StrUtil.isNotBlank(keyword), condition -> condition.likeRight(HpcContract::getContractNo, keyword).or()
                .likeRight(HpcContract::getContractName, keyword).or(CollectionUtils.isNotEmpty(userIdsExtra))
                .in(CollectionUtils.isNotEmpty(userIdsExtra), HpcContract::getUserId, userIdsExtra))
            .and(status != null && (status == 1 || status == 2), condition -> {
                // 合同状态，1｜未履行，2｜履行中
                if (status == 1) {
                    condition.eq(HpcContract::getHpcAccountId, "");
                }
                if (status == 2) {
                    condition.ne(HpcContract::getHpcAccountId, "");
                }
            }).and(contractType != null, condition -> condition.eq(HpcContract::getContractType, contractType))
            .page(pageParam.convert());
        return PageUtils.convert(hpcContracts, HpcContractBO.class);
    }

    @Override
    public IPage<String> listContractUserIdByCondition(PageParam pageParam, SearchContractQuery searchContractQuery) {
        if (pageParam == null) {
            pageParam = new PageParam();
        }
        List<String> levelIds = searchContractQuery.getLevelIds();
        String fuzzyString = searchContractQuery.getFuzzyString();
        Integer contractStatus = searchContractQuery.getContractStatus();
        Integer registerSort = searchContractQuery.getRegisterSort();
        List<String> providerCodes = searchContractQuery.getProviderCodes();

        // 不传条件，返回null
        if (StrUtil.isBlank(fuzzyString) &&
            CollectionUtils.isEmpty(levelIds) &&
            Objects.isNull(HpcContractStatus.instance(contractStatus)) &&
            Objects.isNull(registerSort)) {
            return null;
        }
        return this.baseMapper.listContractUserIdByCondition(pageParam.convert(), levelIds, fuzzyString, contractStatus, registerSort, searchContractQuery.getContractType(), searchContractQuery.getSysUserId(), providerCodes, new Date());
    }

    @Override
    public IPage<HpcContractStructBO> listContractPageByUserIds(PageParam pageParam, SearchContractQuery searchContractQuery) {
        if (pageParam == null) {
            pageParam = new PageParam();
        }
        // 根据客户信息检索合同对应的userIds
        IPage<String> userIdPage = listContractUserIdByCondition(pageParam, searchContractQuery);
        List<String> userIds;
        String sysUserId = searchContractQuery.getSysUserId();
        if (userIdPage != null) {
            userIds = userIdPage.getRecords();
        } else {
            IPage<HpcContract> hpcContractIPage = this.lambdaQuery().select(HpcContract::getUserId).eq(StrUtil.isNotBlank(sysUserId), HpcContract::getCreatorId, sysUserId).groupBy(HpcContract::getUserId).page(pageParam.convert());
            userIdPage = pageParam.convert();
            userIdPage.setTotal(hpcContractIPage.getTotal());
            userIds = hpcContractIPage.getRecords().stream().map(HpcContract::getUserId).collect(Collectors.toList());
        }
        // 合同状态
        HpcContractStatus contractStatus = HpcContractStatus.instance(searchContractQuery.getContractStatus());
        // 构造合同报表
        Page<HpcContractStructBO> contractStructPage = pageParam.convert();
        contractStructPage.setTotal(userIdPage.getTotal());
        contractStructPage.setRecords(new ArrayList<>());
        userIds.forEach(userId -> {
            IPage<HpcContractBO> contractPage = self.listByUserIds(PageParam.getBigPageParam(), Collections.singletonList(userId), null, null, searchContractQuery.getContractType(), searchContractQuery.getProviderCodes(), sysUserId);
            List<HpcContractBO> contractBOS = contractPage.getRecords();
            // 再次进行过滤（已绑定、未绑定）
            if (contractStatus != null) {
                contractBOS = contractBOS.stream().filter(contract -> contractStatus == HpcContractStatus.PERFORM ? StrUtil.isNotBlank(contract.getHpcAccountId()) : StrUtil.isBlank(contract.getHpcAccountId())).collect(Collectors.toList());
            }
            if (CollectionUtil.isEmpty(contractBOS)) {
                return;
            }
            // 根据HPC-ID聚合
            HpcContractStructBO contractStructBO = new HpcContractStructBO();
            contractStructBO.setUserId(userId);
            contractStructBO.setRealName(contractBOS.get(0).getRealName());
            contractStructBO.setPhoneNo(contractBOS.get(0).getPhoneNo());
            contractStructBO.setUserName(contractBOS.get(0).getUserName());
            contractStructBO.setEmail(contractBOS.get(0).getEmail());
            // 设置合同状态文本
            contractStructBO.setHpcContracts(contractBOS);
            contractStructPage.getRecords().add(contractStructBO);
        });
        return contractStructPage;
    }

    @Override
    @Transactional
    public void unboundContract(@Nullable List<String> contractIds, boolean force) {
        if (CollectionUtils.isEmpty(contractIds)) {
            return;
        }
        contractIds.forEach(contractId -> unboundContract(contractId, force));
    }

    private void unboundContract(@Nullable String contractId, boolean force) {
        if (StrUtil.isBlank(contractId)) {
            return;
        }
        // 查询已绑定的合同信息
        HpcContractBO contractBO = self.getById(contractId);
        if (contractBO == null || StrUtil.isBlank(contractBO.getHpcAccountId())) {
            return;
        }
        // 清除缓存
        self.deleteCacheById(contractId);
        // 当合同履行完毕，不允许解绑合同
        HpcContractStatus hpcContractStatus = HpcContractStatus.instance(contractBO.getStatusCode());
        AssertUtil.assertThrow("解绑合同失败，该合同已履行完成，请联系管理员操作", !force && hpcContractStatus == HpcContractStatus.PERFORM_FINISHED);

        HpcContract hpcContract = MapperUtil.nf().map(contractBO, HpcContract.class);
        // 查询是否有其他绑定相同账号的合同信息
        String userId = contractBO.getUserId();
        List<HpcContract> hpcContracts = this.lambdaQuery()
            .eq(HpcContract::getUserId, userId)
            .eq(HpcContract::getHpcAccountId, hpcContract.getHpcAccountId()).list();
        hpcContracts.remove(hpcContract);
        // 为空，直接解绑HPC用户
        if (CollectionUtil.isEmpty(hpcContracts)) {
            // 解绑HPC账号
            HpcAccountUserBo accountUserBO = accountUserService.getMainUserByRecordId(hpcContract.getHpcAccountRecordId());
            if (Objects.nonNull(accountUserBO)) {
                accountUserService.unBoundHpcAccount(accountUserBO.getPid(), force);
            }
        } else {
            hpcContracts.sort((a, b) -> DateUtil.compare(a.getExpireTime(), b.getExpireTime()));
            HpcAccountRecordBo hpcAccountRecordBo = accountRecordService.get(hpcContract.getHpcAccountRecordId());
            if (Objects.nonNull(hpcAccountRecordBo)) {
                // 更新过期时间
                hpcAccountRecordBo.setExpireTime(hpcContracts.get(0).getExpireTime());
                // 更新核时
                hpcAccountRecordBo.setCoreTimeBalance(Arith.sub(hpcAccountRecordBo.getCoreTimeBalance(), hpcContract.getCoreTime()));
                hpcAccountRecordBo.setCoreTimeBalanceTotal(Arith.sub(hpcAccountRecordBo.getCoreTimeBalanceTotal(), hpcContract.getCoreTime()));
                accountRecordService.update(hpcAccountRecordBo);
            }
        }
        // 解绑合同
        hpcContract.setHpcAccount("");
        hpcContract.setHpcAccountId("");
        hpcContract.setHpcAccountRecordId("");
        this.updateById(hpcContract);
    }

    @Override
    @Transactional
    public void createAndBoundQCloudHpc(QingCloudContractUsableQuery qingCloudContractUsableQuery) {
        HpcContractBO hpcContractBO = self.getById(qingCloudContractUsableQuery.getContractId());
        AssertUtil.assertThrow("合同不存在", Objects.isNull(hpcContractBO));
        AssertUtil.assertThrow("合同类型错误", !Objects.equals(HpcProviderType.QY.value(), hpcContractBO.getProviderCode()));

        // 创建青云账号，收到创建
        QCloudHpcAccountQuery qCloudHpcAccountQuery = new QCloudHpcAccountQuery();
        qCloudHpcAccountQuery.setSlurmAccount(qingCloudContractUsableQuery.getSlurmAccount());
        qCloudHpcAccountQuery.setHpcAccountName(qingCloudContractUsableQuery.getHpcAccount());
        qCloudHpcAccountQuery.setCoreTime(hpcContractBO.getCoreTime());
        HpcAccountBo hpcAccountBo = qcHpcAccountService.createOrUpdateQCloudAccount(qCloudHpcAccountQuery);

        HpcAccountRecordBo hpcAccountRecordBo = accountRecordService.searchOneByHpcAccountId(hpcAccountBo.getHpcAccountId());
        if (Objects.nonNull(hpcAccountRecordBo)) {
            String userId = hpcAccountRecordBo.getUserId();
            AssertUtil.assertThrow("绑定失败，合同无效", !Objects.equals(hpcContractBO.getUserId(), userId));
            HpcAccountUserBo hpcAccountUserBo = accountUserService.getMainUserByRecordId(hpcAccountRecordBo.getPid());
            CreateHpcContractQuery createHpcContractQuery = new CreateHpcContractQuery();
            createHpcContractQuery.setHpcAccountUserId(hpcAccountUserBo.getPid());
            createHpcContractQuery.setContractId(hpcContractBO.getPid());
            self.boundContract(createHpcContractQuery);
        } else {
            // 合同使用青云账号
            ContractUsableQuery contractUsableQuery = new ContractUsableQuery();
            contractUsableQuery.setContractId(hpcContractBO.getPid());
            contractUsableQuery.setHpcAccountId(hpcAccountBo.getHpcAccountId());
            contractUsableQuery.setSlurmAccount(qingCloudContractUsableQuery.getSlurmAccount());
            self.contractUsable(contractUsableQuery);
        }

    }

}
